package org.hxzon.cyclone;

import java.util.ArrayList;
import java.util.List;

import org.hxzon.cyclone.pcontacts.ParticleContact;
import org.hxzon.cyclone.pcontacts.ParticleContactGenerator;
import org.hxzon.cyclone.pcontacts.ParticleContactResolver;
import org.hxzon.cyclone.pfgen.ParticleForceRegistry;

//pworld.h,pworld.cpp
public class ParticleWorld {

    protected List<Particle> particles;

    /**
     * True if the world should calculate the number of iterations
     * to give the contact resolver at each frame.
     */
    protected boolean calculateIterations;

    /**
     * Holds the force generators for the particles in this world.
     */
    protected ParticleForceRegistry registry;

    /**
     * Holds the resolver for contacts.
     */
    protected ParticleContactResolver resolver;

    /**
     * Contact generators.
     */
    protected List<ParticleContactGenerator> contactGenerators;

    /**
     * Holds the list of contacts.
     */
    protected List<ParticleContact> contacts;

    /**
     * Creates a new particle simulator that can handle up to the
     * given number of contacts per frame. You can also optionally
     * give a number of contact-resolution iterations to use. If you
     * don't give a number of iterations, then twice the number of
     * contacts will be used.
     */
    public ParticleWorld(int iterations) {
        this.resolver = new ParticleContactResolver(iterations);
        contacts = new ArrayList<ParticleContact>();
        calculateIterations = (iterations == 0);
    }

    public ParticleWorld() {
        this(0);
    }

    /**
     * Deletes the simulator.
     */
    public void destroy() {
        //delete[] contacts;
    }

    /**
     * Calls each of the registered contact generators to report
     * their contacts. Returns the number of generated contacts.
     */
    public int generateContacts() {
        for (ParticleContactGenerator g : contactGenerators) {
            g.addContact(contacts);
        }
        return contacts.size();
    }

    /**
     * Integrates all the particles in this world forward in time
     * by the given duration.
     */
    public void integrate(double duration) {
        for (Particle p : particles) {
            // Remove all forces from the accumulator
            p.integrate(duration);
        }
    }

    /**
     * Processes all the physics for the particle world.
     */
    public void runPhysics(double duration) {
        // First apply the force generators
        registry.updateForces(duration);

        // Then integrate the objects
        integrate(duration);

        // Generate contacts
        int usedContacts = generateContacts();

        // And process them
        if (usedContacts > 0) {
            if (calculateIterations) {
                resolver.setIterations(usedContacts * 2);
            }
            resolver.resolveContacts(contacts, duration);
        }
    }

    /**
     * Initializes the world for a simulation frame. This clears
     * the force accumulators for particles in the world. After
     * calling this, the particles can have their forces for this
     * frame added.
     */
    public void startFrame() {
        for (Particle p : particles) {
            // Remove all forces from the accumulator
            p.clearAccumulator();
        }
    }

    public List<Particle> getParticles() {
        return particles;
    }

    public List<ParticleContactGenerator> getContactGenerators() {
        return contactGenerators;
    }

    public ParticleForceRegistry getForceRegistry() {
        return registry;
    }

}
